package hydrap2p.proxy.manager;

import hydrap2p.ServiceLocation;
import hydrap2p.logging.Level;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Map;

import org.apache.thrift.TException;

public class ActiveConnections {

	private DoubleMap<ServiceLocation, ServiceLocation, Connection> connections;
	private ManagerConnectionProcessor managerconnection;
	public ActiveConnections(ManagerConnectionProcessor managerconnection)
	{
		connections=new DoubleMap<ServiceLocation, ServiceLocation, Connection>();
		this.managerconnection=managerconnection;
	}
	
	public void sendData(ServiceLocation from, ServiceLocation dest, byte[] data) throws IOException
	{
		//ProxyLogging.log(Level.DEBUG, "ProxyManager - ActiveConnections", "Sending data from "+from+" to "+dest);
		Connection target;
		synchronized(connections)
		{
			target=connections.get(dest, from);
			if (target==null)
			{
				//ProxyLogging.log(Level.DEBUG, "ProxyManager - ActiveConnections", "Creating new connection from "+from+" to "+dest);
				target=new Connection(from, dest);
				connections.put(dest, from, target);
				new Thread(target,target.toString()).start();
			}
		}
		
		target.sendData(data);
	}

	
	private void deleteConnection(ServiceLocation from, ServiceLocation dest)
	{
		//ProxyLogging.log(Level.DEBUG, "ProxyManager - ActiveConnections", "Removing connection from "+from+" to "+dest);

		synchronized(connections)
		{
			connections.remove(dest, from);
		}
	}
	
	private class Connection implements Runnable
	{
		private Socket connection;
		private ServiceLocation from;		// client
		private ServiceLocation dest;		// server
		public Connection(ServiceLocation from, ServiceLocation dest) throws IOException
		{
			this.connection=new Socket(dest.address, dest.port);
			this.from=from;
			this.dest=dest;
		}
		
		public void sendData(byte[] data) throws IOException
		{
			try {
				synchronized(connection.getOutputStream())
				{
					connection.getOutputStream().write(data);
				}
				//ProxyLogging.log(Level.DEBUG, "ProxyManager - ActiveConnections", "Sent data from "+from+" to "+dest);

			} catch (IOException e) {
				// Failure to communicate to server program
				try {
					connection.close();
				} catch (IOException e1) {
					// Give up
				}
				deleteConnection(from, dest);
				//ProxyLogging.log(Level.DEBUG, "ProxyManager - ActiveConnections", "Failed to send data from "+from+" to "+dest+": "+e.getLocalizedMessage());
				throw new IOException("Failure to communicate to server");
			}
		}
		
		@Override
		public void run()
		{
			byte[] temp=new byte[16*1024];
			int size=0;
			while (!connection.isClosed() && size>=0)
			{
				try {
					size=connection.getInputStream().read(temp);
				} catch (IOException e) {
					// disconnected
					try {
						connection.close();
					} catch (IOException e1) {
						// Give up
						break;
					}
				}
				
				if (size>0)
				{
					try {
						//ProxyLogging.log(Level.DEBUG, "ProxyManager - ActiveConnections", "Returning data from "+dest+" to "+from);

						managerconnection.returnData(dest, from, temp, size);
					} catch (IOException e) {
						//ProxyLogging.log(Level.DEBUG, "ProxyManager - ActiveConnections", "Failed to return data from "+dest+" to "+from+": "+e.getLocalizedMessage());
						// Failure to communicate with peer, somehow
						try {
							connection.close();
						} catch (IOException e1) {
							// Give up
							break;
						}
					}
				}
			}
			//ProxyLogging.log(Level.DEBUG, "ProxyManager - ActiveConnections", "Closing down connection from "+from+" to "+dest);
			deleteConnection(from, dest);
			try {
				managerconnection.closeDataConnection(from, dest);
			} catch (IOException e) {
				// Failure to communicate with peer
			}
		}
		
		@Override
		public String toString()
		{
			return "ActiveConnection: "+from.address+":"+from.port+" -> "+dest.address+":"+dest.port;
		}
	}
}
